package com.shujia;

import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.JavaIntObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;


/**
 * 注意：
 * 和继承UDF步骤基本一致，而GenericUDF需要实现其三个函数
 *
 * 需求：
 * 针对传入的字符串，返回其字符串的长度
 *
 * 用法：
 * select mylength('AAA')  => 3
 *
 * 注意：
 *      添加jar包时，需要重新开启会话，然后再去通过add jar 添加
 *
 *  GenericUDF函数特点：
 *      1.传入一行参数，同时也会返一行参数
 *      2.使用时，可以判断参数长度及其类型
 *
 *  add jar /usr/local/soft/jars/hiveCode-1.0.jar
 *
 *  create temporary function mylength as 'com.shujia.NewUDF';
 *
 */

/**
 *
 * desc function mylength;
 * desc function extended mylength;
 *
 */
@Description(
        name = "mylength",
        value = "mylength('str') return length(str)",
        extended = "select mylength('AAA')"
)
public class NewUDF extends GenericUDF {

    /**
     * initialize:在对象构建时，会进行实例化，可以在该函数中针对传入值，进行判断，
     * 判断的方式有两种：
     * ① 类型：
     * ② 长度：
     *
     * @param arguments ObjectInspector是一个类或者是对象的检查器，arguments表示的是hive中使用时，传入的参数
     * @return ObjectInspector 表示hive中函数执行完成后，返回值的类型，对应样例中的 3类型
     * @throws UDFArgumentException
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        /**
         * ObjectInspector.Category category = arguments[0].getCategory();
         * public static enum Category {
         *         PRIMITIVE, => 表示HIVE中的普通数据类型
         *         LIST => 表示HIVE中的Array数据类型
         *         MAP => 表示HIVE中的Map数据类型
         *         STRUCT  => 表示HIVE中的STRUCT数据类型
         */

        if (arguments.length != 1) {
            throw new UDFArgumentLengthException("当前函数只能有一个参数值，并且该参数值类型是String类型");
        }

        if (!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)) {
            // public UDFArgumentTypeException(int argumentId, String message)
            throw new UDFArgumentTypeException(1, "当前函数传入值类型为String");
        }

//        return PrimitiveObjectInspectorFactory.writableIntObjectInspector;

        return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }

    /**
     * evaluate:函数内部主要用于定义自定义函数的逻辑以及返回值
     *
     * @param arguments hive中传入的参数值
     * @return
     * @throws HiveException
     */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        String col1 = arguments[0].get().toString();
//        System.out.println(col1);
        return col1.length();
    }

    /**
     * getDisplayString 主要用于方法执行逻辑展示
     *
     * @param children
     * @return
     */
    @Override
    public String getDisplayString(String[] children) {
        return "nothing to do ...";
    }
}
